from expression import expression, environment
from jsobject import jsobject, closure
from customExceptions import *
import sys
sys.path.append(sys.path[0] + "/../LLVM")
from llvmhelper import llvmHelper
from llvm import *
from commonRoutines import commonRoutines

class fieldLookup(expression):
	def __init__(self, obj, name):
		self.obj = obj
		self.name = name
		self.objeframe = None
		self.memNum = None
		self.this = False

	def evaluate(self, env):
		obj = self.obj.evaluate(env)
		#print "name: ", self.name , "obj: " , str(obj)
		if(isinstance(obj, jsobject)):
			c = obj.access(self.name.__str__())
			#print "HERE: ", str(c)
			return c
		else:
			raise IncorrectArgumentExcpt("fieldLookup",type(jsobject), type(obj))

	def __str__(self):
		return "fieldLookup(" + str(self.obj) + "." + str(self.name) + ")"

	def traverse(self, helper):
		self.memNum = helper.getMemberDataNumber(self.name)
		self.obj.traverse(helper)
		#sys.stderr.write("Check Obj Eframe: " + str(self.obj) + "\n")
		#sys.stderr.write("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n") 
		self.objeframe = self.obj.eframe
		#if(self.objeframe.name == "this"):
		#	self.this = True
		#print "Self.eframe in field lookup = " + str(self.eframe) + "\n"

	def toLLVM(self, helper):
		helper.descend("fieldLookUp")
		c = commonRoutines(helper)
		
		self.name
		#Registers
		slotsReg = helper.getNextReg()
		
		intToPtrReg = helper.getNextReg()
		slotsPtrReg = helper.getNextReg()
		slotsPtrToIntReg = helper.getNextReg()


		#sys.stderr.write("Object lookup: " + str(self.objeframe) +"\n")
		#sys.stderr.write("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n") 
		if(self.objeframe == "%this"):
			objectReg = self.objeframe
		elif(self.objeframe != None):
			objectReg = c.fetchVar(self.objeframe[1], self.objeframe[2])

		
		
		intToPtrReg = c.testAndUnmaskPointer(objectReg, "%jsobject*")
		helper.addLLVMInst(llvm_gep(slotsPtrReg,intToPtrReg,"%jsobject*",  0 , "i32" , 1 , "i32"), "Get the pointer to slots")
		helper.addLLVMInst(llvm_load(slotsReg, slotsPtrReg, "%slots*"), "load actual slots*")
		helper.addLLVMInst(llvm_ptrtoint(slotsPtrToIntReg, slotsReg, "%slots*", "i32"), "Turn slots into an i32")
		fieldReg = c.fetchSlotsVar(slotsPtrToIntReg,self.memNum) 
		helper.ascend(fieldReg)





class fieldMutation(expression):
	def __init__(self, obj, name, value):
		self.obj = obj
		self.name = name
		self.value = value
		self.objeframe = None
		self.memNum = None
	def evaluate(self, env):
		obj = self.obj.evaluate(env)
		if(isinstance(obj, jsobject)):
			return obj.mutate(self.name.__str__(), self.value.evaluate(env))
		else:
			raise IncorrectArgumentExcpt("fieldMutation",type(jsobject), type(obj))
	def __str__(self):
		return "fieldMutation(" + str(self.obj) + "," + str(self.name) + ","+  str(self.value) + ")"

	def traverse(self, helper):
		self.memNum = helper.getMemberDataNumber(self.name)
		self.obj.traverse(helper)
		self.value.traverse(helper)
		self.objeframe = self.obj.eframe
		#print "Self.eframe in field lookup = " + str(self.eframe) + "\n"
		
	def toLLVM(self, helper):
		helper.descend("fieldMutation")
		self.value.toLLVM(helper)
		c = commonRoutines(helper)

		#Registers
		slotsReg = helper.getNextReg()
		eframReg = helper.getNextReg()
		eframepoint = helper.getNextReg()
		ScopeReg = helper.getCurrentScopeReg()
		closureReg = None
		newSlots = helper.getNextReg()
		nameReg = helper.getNextReg()

		intToPtrReg = helper.getNextReg()
		slotsPtrReg = helper.getNextReg()
		slotsPtrToIntReg = helper.getNextReg()
	

		#sys.stderr.write("Object lookup: " + str(self.objeframe) +"\n")
		if(self.objeframe == "%this"):
			objectReg = self.objeframe
		elif(self.objeframe != None):
			objectReg = c.fetchVar(self.objeframe[1], self.objeframe[2])
		self.memNum = helper.getMemberDataNumber(self.name)
		self.obj.traverse(helper)
		self.objeframe = self.obj.eframe
			
#		temp2 = helper.getNextReg()
		#helper.addLLVMInst(llvm_ptrtoint(temp2, malloced, "%jsobject*", "i32"))
#		helper.debugPrint(objectReg, "c")
		
		#helper.addLLVMInst(llvm_inttoptr(intToPtrReg, objectReg, "i32", "%jsobject*"))
		intToPtrReg = c.testAndUnmaskPointer(objectReg, "%jsobject*")
		helper.addLLVMInst(llvm_gep(slotsPtrReg,intToPtrReg,"%jsobject*",  0 , "i32" , 1 , "i32"), "Get the pointer to slots")
		
#		temp1 = helper.getNextReg()
#		helper.addLLVMInst(llvm_ptrtoint(temp1, slotsPtrReg, "%slots**", "i32"))
#		helper.debugPrint(temp1, "c")
		
		helper.addLLVMInst(llvm_load(slotsReg, slotsPtrReg, "%slots*"), "load actual slots*")
		helper.addLLVMInst(llvm_ptrtoint(slotsPtrToIntReg, slotsReg, "%slots*", "i32"), "Turn slots into an i32")
		fieldReg = c.mutateSlotsVar(slotsPtrToIntReg,self.memNum, helper.getReturns()[0]) 
		helper.ascend(fieldReg)
			
# applies closures
class methodCall(expression):
	def __init__(self, obj, name, args):
		self.obj = obj
		self.name = name
		self.args = args
	def evaluate(self, env):
		obj = self.obj.evaluate(env)
		if(isinstance(obj, jsobject)):
			c =  obj.access(self.name.__str__()) #hopefully, a closure
			if(isinstance(c, closure)):
				params = c.getParams()
				#append arguments, with correct bindings, to new enviroment
				#call function body

				if(len(self.args) != len(params)):
					raise GeneralException("Methos call to Function " + str(self.name) + " Has " + str(len(self.args)) + " args expected "+ str(len(params)))
				else:
					newEnv = c.getEnv()
					for arg, param in zip(self.args, params):
						# I think this line should do this:
						# for each parameter in the function, add a variable('x') to the envrionment
						# 'x' has the value of the associated argument, passed in the application
						# In addition, the new environment ('newEnv') is updated as params are added
						# but the old env ('env') is not updated - not that all the argument evaluations
						# use 'env'. I think this is the way to do it.
						newEnv = environment(param, arg.evaluate(env), newEnv)
					
					try:
						newEnv.getVarRef("this")
					except VarNotFoundExcpt:
						newEnv = environment("this", obj, newEnv)
					
							
					try:
						c.evaluate(newEnv)
						return void()
					except ReturnExcpt, ret:
						return ret.getValue()

			else:
				raise IncorrectArgumentExcpt("methodCall.closure",type(closure), type(c))
		else:
			raise IncorrectArgumentExcpt("methodCall.object",type(jsobject), type(obj))
			
	def __str__(self):
		retval = "methodCall(" + str(self.obj) + ", " + str(self.name)+ ", ["
		for a in self.args:
			retval += str(a) + ", "
		retval += "])"
		return retval

	def traverse(self, helper):
		self.memNum = helper.getMemberDataNumber(self.name)
		self.obj.traverse(helper)
		self.objeframe = self.obj.eframe
		for a in self.args:
			a.traverse(helper)
	
	def toLLVM(self, helper):
		helper.descend("methodCall")
		c = commonRoutines(helper)

		#Registers
		slotsReg = helper.getNextReg()
		
		intToPtrReg = helper.getNextReg()
		slotsPtrReg = helper.getNextReg()
		slotsPtrToIntReg = helper.getNextReg()


		#sys.stderr.write("Object lookup: " + str(self.objeframe) +"\n")
		if(self.objeframe != None):
			objectReg = c.fetchVar(self.objeframe[1], self.objeframe[2])
		
		
		intToPtrReg = c.testAndUnmaskPointer(objectReg, "%jsobject*")
		helper.addLLVMInst(llvm_gep(slotsPtrReg,intToPtrReg,"%jsobject*",  0 , "i32" , 1 , "i32"), "Get the pointer to slots")
		helper.addLLVMInst(llvm_load(slotsReg, slotsPtrReg, "%slots*"), "load actual slots*")
		helper.addLLVMInst(llvm_ptrtoint(slotsPtrToIntReg, slotsReg, "%slots*", "i32"), "Turn slots into an i32")
		fieldReg = c.fetchSlotsVar(slotsPtrToIntReg,self.memNum) 
		
		for a in self.args:
			a.toLLVM(helper)
			
		
		mallocReg = helper.getNextReg()
		argArrayReg = helper.getNextReg()
		pointerToNumReg = helper.getNextReg()
		resultReg = helper.getNextReg()
		
		helper.addLLVMLine("; Calling " + self.name)
		
		helper.addLLVMInst(llvm_malloc(mallocReg, "{i32, [ " + str(len(self.args)) + " x i32 ]}"), "Creating array for args")
		helper.addLLVMInst(llvm_bitcast(argArrayReg, mallocReg, "{i32, [ " + str(len(self.args)) + " x i32 ]}*", "%valArray*"), "Bitcasting to the right type")
		helper.addLLVMInst(llvm_gep(pointerToNumReg, argArrayReg, "%valArray*", 0, "i32", 0), "Get location of array size")
		helper.addLLVMInst(llvm_store(len(self.args), pointerToNumReg), "Thow in number (not shifted!)")
	
		argValues = helper.getReturns()
	
		for a in range(len(self.args)):
			argAddr = helper.getNextReg()
			
			helper.addLLVMInst(llvm_gep(argAddr, argArrayReg, "%valArray*", 0, "i32", 1, "i32", a), "Get location of " + str(a) + "th argument")
			helper.addLLVMInst(llvm_store(argValues[a], argAddr), "Good god, I hope those are I32s. Adding arg")
		
		
		helper.setLabel(helper.getLastLabel())
		
		helper.addLLVMInst(llvm_call("method_dispatch", "i32", resultReg, fieldReg, "i32", objectReg,"i32", argArrayReg, "%valArray*"), "Call Dispatch!")
		
		helper.setLabel(helper.getLastLabel())
		helper.ascend(resultReg)

		
